iT邦幫忙

2022 iThome 鐵人賽

DAY 12
0
Modern Web

擁抱 .Net Core系列 第 12

[Day12] 綁定物件以及常見的CoufigurationSource - Configuration - 3

  • 分享至 

  • xImage
  •  

我們在使用Config的時候,常見會有幾種方式

  1. 直接注入IConfiguration的物件,並且用Key取得其值。
  2. 綁定到物件上,並且注入該物件
  3. 選項模式(明天會提到)

直接注入

{
  "line": {
    "channelId": "line_channel_id",
    "channelSecret": "line_channel_secret",
    "info": {
      "name": "line_bot_name",
      "pictureUrl": "line_bot_picture_url"
    }
  },
  "facebook": {
    "pageId": "facebook_page_id",
    "accessToken": "facebook_page_access_token",
    "appSecret": "facebook_app_secret"
  },
  "telegram": {
    "botToken": "telegram_bot_token"
  }
}

直接注入IConfiguration 的方式
Program.cs

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

var configuration = new ConfigurationBuilder()
    .SetBasePath(Directory.GetCurrentDirectory())
    .AddJsonFile("appsettings.json")
    .Build();

var serviceProvider = new ServiceCollection()
    .AddSingleton<IConfiguration>(configuration)
    .AddTransient<LineService>()
    .BuildServiceProvider();

var lineService = serviceProvider.GetService<LineService>();
lineService!.PrintLineAppName();

LineService.cs

public class LineService
{
    private readonly IConfiguration _configuration;

    public LineService(IConfiguration configuration)
    {
        _configuration = configuration;
    }

    public void PrintLineAppName()
    {
        Console.WriteLine(_configuration["line:info:name"]);
    }
}

我們透過_configuration["line:info:name"] 取得了line_bot_name
但是說真的這方式並不這麼推薦使用,原因大概如下

  1. 容易不小心打錯字或搞錯階層
  2. 少了intellisense 的幫助,開發速度下降,加上容易犯第一點

我會用這種方式通常有兩種原因

  1. 偷懶懶得建立一個物件包一個只用一樣的Config
  2. 在開發初期快速使用

較常見的是綁定到物件

綁定到物件

先添加我們要綁定的物件LineSetting.cs

public class LineSetting
{
    public string ChannelId { get; set; }
    public string ChannelSecret { get; set; }
    public LineSetting.LineInfo Info { get; set; }
    
    public class LineInfo
    {
        public string Name { get; set; }
        public string PictureUrl { get; set; }
    }
}

並且調整LineService.cs為注入LineSetting.cs的板本

public class LineService
{
    private readonly LineSetting _lineSetting;

    public LineService(LineSetting lineSetting)
    {
        _lineSetting = lineSetting;
    }

    public void PrintLineAppName()
    {
        Console.WriteLine(_lineSetting.Info.Name);
    }
}

主要有兩種方式進行綁定

Get<T>

Program.cs

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

var configuration = new ConfigurationBuilder()
    .SetBasePath(Directory.GetCurrentDirectory())
    .AddJsonFile("appsettings.json")
    .Build();

var lineSetting = configuration
    .GetSection("line")
    .Get<LineSetting>();

var serviceProvider = new ServiceCollection()
    .AddSingleton(lineSetting)
    .AddTransient<LineService>()
    .BuildServiceProvider();

var lineService = serviceProvider.GetService<LineService>();
lineService!.PrintLineAppName();

我們使用了 IConfiguration.GetSection("line") 來取得line這個結點的IConfigurationGetSection
然後透過了IConfiguration.Get<LineSetting>()方法取得了
LineSetting的物件,Get方法會直接幫你Return一個底下物件綁定好的物件

Bind

我們調整Program.cs
改使用Bind的方式來取得設定

// ... ConfigBuilder

var lineSetting = new LineSetting();

configuration
    .GetSection("line")
    .Bind(lineSetting);

// ... DI Setting

可以看見Bind跟Get其實相差不遠
只是Bind是針對既有的執行個體做綁定
Get 則是會回傳一個新的執行個體

常見的ConfigurationSource

EnvironmentVariablesConfigurationSource

這邊的環境變數指的不完全是程式的環境變數
包括系統的環境變數也被包含在其中
https://ithelp.ithome.com.tw/upload/images/20220923/20109549jMkB1oi3l9.png
具體來說我不常用到他

CommandLineConfigurationSource

顧名思義,可以讀取Cli 的值作為其Config來源

program.cs

var mapping = new Dictionary<string,string>()
{
    ["--line"] = "line",
    ["-f"] = "fb"
};

var configuration = new ConfigurationBuilder()
    .AddCommandLine(args, mapping)
    .AddCommandLine(args)
    .Build();

Console.WriteLine(configuration["line:info:name"]);
Console.WriteLine(configuration["line"]);
Console.WriteLine(configuration["fb"]);
dotnet run line:info:name=linebot --line=lineChannelId -x FB_Page
Id

可以看見我們透過自訂mapping的方式將commandLine參數與對應的setting mapping在一起,也可以透過path的方式指定節點的值。

MemoryConfigurationSource

簡單方便粗暴

主要透過一個IEnumerable<KeyValuePair<stirng,string>> 作為資料來源

using Microsoft.Extensions.Configuration;

var memoryData = new Dictionary<string,string>()
{
    ["line:channelId"] = "ChannelId",
    ["line:channelSecret"] = "ChannelSecret",
    ["line:info:name"] = "Name",
    ["line:info:iconUrl"] = "IconUrl",
};

var configuration = new ConfigurationBuilder()
    .AddInMemoryCollection(memoryData)
    .Build();

Console.WriteLine(configuration["line:channelId"]);
Console.WriteLine(configuration["line:channelSecret"]);
Console.WriteLine(configuration["line:info:name"]);
Console.WriteLine(configuration["line:info:iconUrl"]);

使用上基本上通常就是建個Dictionary丟進去就對了

FileConfiguarionSource

設定來源是檔案類型的
FileConfiguarionSource.cs

public abstract class FileConfigurationSource : IConfigurationSource
{
    public IFileProvider FileProvider { get; set; }

    public string Path { get; set; }

    public bool Optional { get; set; }

    public bool ReloadOnChange { get; set; }

    public int ReloadDelay { get; set; } = 250;
    
    // others...
}

可以看見有IFileProvider,可以決定要提供的檔案的實際提供者
ConfigurationBuilder會優先取設定的IFileProvider,若無則建立一個PhysicalFileProvider作為FileProvider

比較有趣的是OptionalReloadOnChangeReloadDelayOnLoadException這4個屬性

  • Optional所代表的是這個設定來源是不是可選的,設為true時代表即便沒有這個設定檔也沒關係
    可以在分環境的appSettings.json看見其作用。假設當前環境是 Stage,即便你忘了放appsettings.developer.json 也不會報錯,因為他的Optional是設為true的
  • ReloadOnChangeReloadDelay: 顧名思義,就是當檔案更新時要不要重讀設定檔,以及要延遲多久重新載入
    主要就是透過IFileProvider.Watch來達到

常見有好幾種,都是繼承自FileConfiguarionSource

  • JsonConfiguarionSource
  • XmlConfiguarionSource
  • IniConfiguarionSource

主要介紹一下在dotnet 6 Host中設定json的方式

builder.Configuration.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);

對比dotnet core 5以下的設定方式

builder.ConfigureAppConfiguration(builder=>builder.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true))

語意上清楚了多


上一篇
[Day11] 談談設定檔(組態) - Configuration - 2
下一篇
[Day13] 選項模式 - Configuration - 4
系列文
擁抱 .Net Core30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言